<template>
  <div v-if="modelValue" class="fixed inset-0 z-[100] flex items-center justify-center">
    <!-- 背景遮罩 -->
    <div class="absolute inset-0 bg-black/30 backdrop-blur-sm" @click="handleHangup" />

    <!-- 面板主体 -->
    <div class="relative w-full h-full flex items-center justify-center select-none">
      <!-- 顶部状态气泡 -->
      <div class="absolute top-12 flex items-center gap-2 px-4 h-9 rounded-full bg-white/90 text-gray-700 shadow-md ring-1 ring-black/5">
        <span class="inline-block h-2 w-2 rounded-full" :class="statusDotClass" />
        <span class="text-sm">{{ statusText }}</span>
      </div>

      <!-- 中心大圆与装饰刻度 -->
      <div class="relative">
        <!-- 大圆 -->
        <div
          class="relative rounded-full shadow-xl flex items-center justify-center"
          :style="{
            width: circleInnerPx + 'px',
            height: circleInnerPx + 'px',
            background: 'radial-gradient(closest-side, ' + innerBgFrom + ', ' + innerBgTo + ')',
            boxShadow: '0 20px 80px ' + glowColor
          }"
        >
          <div class="absolute inset-0 rounded-full ring-8 ring-white/80" />
          <button
            class="relative h-16 w-16 rounded-full text-white shadow-lg focus:outline-none"
            :class="centerBtnClass"
            aria-label="microphone"
          >
            <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" class="h-6 w-6 mx-auto" fill="currentColor">
              <path d="M12 14a3 3 0 003-3V5a3 3 0 10-6 0v6a3 3 0 003 3zm5-3a5 5 0 11-10 0H5a7 7 0 0014 0h-2zM11 19h2v3h-2v-3z" />
            </svg>
          </button>
        </div>
      </div>

      <!-- 下方波形区域（Canvas） -->
      <div class="absolute left-6 right-6" :style="{ bottom: '96px' }">
        <canvas ref="canvasRef" class="h-28 w-full"></canvas>
      </div>

      <!-- 挂断按钮 -->
      <button
        class="absolute right-6 bottom-8 h-14 w-14 rounded-full bg-red-500 text-white shadow-2xl shadow-red-500/30 active:scale-95 transition"
        @click="handleHangup"
        aria-label="hangup"
      >
        <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" class="h-6 w-6 mx-auto" fill="currentColor">
          <path d="M3 5a2 2 0 012-2h3.28a1 1 0 01.948.684l1.498 4.493a1 1 0 01-.502 1.21l-2.257 1.13a11.042 11.042 0 005.516 5.516l1.13-2.257a1 1 0 011.21-.502l4.493 1.498a1 1 0 01.684.949V19a2 2 0 01-2 2h-1C9.716 21 3 14.284 3 6V5z"/>
        </svg>
      </button>
    </div>
  </div>
  </template>

<script setup lang="ts">
import { computed, onMounted, onBeforeUnmount, ref, watch } from 'vue'
import VoiceCallController, { type VoiceCallStatus } from '@/utils/voiceCallController'
// import VoiceCallController, { type VoiceCallStatus } from '@/utils/voiceCallController'

interface Props {
  /** v-model */
  modelValue: boolean
  /** 状态：listening | recognizing | speaking */
  status?: 'listening' | 'recognizing' | 'speaking'
  baseURL?: string
  chatId?: string
}

const props = withDefaults(defineProps<Props>(), {
  modelValue: false,
  status: 'recognizing',
  baseURL: (import.meta as any).env?.VITE_APP_BASE_API || '',
  chatId: ''
})

const emit = defineEmits<{
  (e: 'update:modelValue', v: boolean): void
  (e: 'hangup'): void
}>()

const circleInnerPx = 280
// 外圈装饰已移除
// const circleOuterPx = 360

const canvasRef = ref<HTMLCanvasElement>()
let audioCtx: AudioContext | null = null
let micAnalyser: AnalyserNode | null = null
let playAnalyser: AnalyserNode | null = null
let activeAnalyser: AnalyserNode | null = null
let micStream: MediaStream | null = null
let rafId: number | null = null

const uiMode = ref<'recognizing' | 'speaking' | 'listening'>('recognizing')
const controllerStatus = ref<VoiceCallStatus>('idle')

const statusText = computed(() => {
  switch (controllerStatus.value) {
    case 'speaking':
      return '正在回复…'
    case 'recognizing':
      return '识别中…'
    case 'ready':
      return '请开始讲话…'
    default:
      return '思考中…'
  }
})

const statusDotClass = computed(() => ({
  'bg-[#6C5CE7]': uiMode.value !== 'speaking',
  'bg-[#1FB2A6]': uiMode.value === 'speaking'
}))

// 装饰条颜色（已移除装饰条）

const innerBgFrom = computed(() => (uiMode.value === 'speaking' ? 'rgba(31,178,166,0.25)' : 'rgba(108,92,231,0.25)'))
const innerBgTo = computed(() => (uiMode.value === 'speaking' ? 'rgba(31,178,166,0.1)' : 'rgba(108,92,231,0.1)'))
const glowColor = computed(() => (uiMode.value === 'speaking' ? 'rgba(31,178,166,0.35)' : 'rgba(108,92,231,0.35)'))
const centerBtnClass = computed(() => (uiMode.value === 'speaking' ? 'bg-[#1FB2A6]' : 'bg-[#6C5CE7]'))

// 已移除装饰条函数

function handleHangup() {
  emit('update:modelValue', false)
  emit('hangup')
}

function drawLoop() {
  if (!canvasRef.value || !activeAnalyser) return
  const canvas = canvasRef.value
  const dpr = window.devicePixelRatio || 1
  const width = canvas.clientWidth
  const height = canvas.clientHeight
  canvas.width = Math.floor(width * dpr)
  canvas.height = Math.floor(height * dpr)
  const ctx = canvas.getContext('2d')!
  ctx.scale(dpr, dpr)

  const bufferLength = 1024
  const dataArray = new Uint8Array(bufferLength)
  activeAnalyser.fftSize = bufferLength * 2

  const render = () => {
    if (!activeAnalyser || !canvasRef.value) return
    activeAnalyser.getByteTimeDomainData(dataArray)
    ctx.clearRect(0, 0, width, height)

    const color = uiMode.value === 'speaking' ? '#1FB2A6' : '#6C5CE7'

    // 中线
    ctx.strokeStyle = color + '66'
    ctx.lineWidth = 1
    ctx.beginPath()
    ctx.moveTo(0, height / 2)
    ctx.lineTo(width, height / 2)
    ctx.stroke()

    // 波形
    ctx.strokeStyle = color
    ctx.lineWidth = 2
    ctx.beginPath()
    const sliceWidth = width / bufferLength
    let x = 0
    for (let i = 0; i < bufferLength; i++) {
      const v = dataArray[i] / 128.0
      const y = (v * height) / 2
      if (i === 0) ctx.moveTo(x, y)
      else ctx.lineTo(x, y)
      x += sliceWidth
    }
    ctx.stroke()

    rafId = requestAnimationFrame(render)
  }
  rafId = requestAnimationFrame(render)
}

async function startMic() {
  if (audioCtx) return
  audioCtx = new (window.AudioContext || (window as any).webkitAudioContext)()
  micStream = await navigator.mediaDevices.getUserMedia({
    audio: { echoCancellation: true, noiseSuppression: true, autoGainControl: true },
  })
  const source = audioCtx.createMediaStreamSource(micStream)
  micAnalyser = audioCtx.createAnalyser()
  source.connect(micAnalyser)
  activeAnalyser = micAnalyser
  drawLoop()
}

function stopMic() {
  if (rafId) cancelAnimationFrame(rafId)
  rafId = null
  try { micStream?.getTracks().forEach(t => t.stop()) } catch {}
  try { audioCtx?.close() } catch {}
  audioCtx = null
  micAnalyser = null
  playAnalyser = null
  activeAnalyser = null
  micStream = null
}

let controller: VoiceCallController | null = null

onMounted(() => {
  if (props.modelValue) {
    startMic()
    controller = new VoiceCallController({
      onStatus: (s) => {
        controllerStatus.value = s
        uiMode.value = s === 'speaking' ? 'speaking' : 'recognizing'
      },
    })
    const chatId = props.chatId || `chat_${Math.random().toString(36).slice(2, 8)}`
    controller.start({ baseURL: props.baseURL || '', chatId }).catch(() => {})
  }
})

onBeforeUnmount(() => {
  stopMic()
  controller?.stop()
  controller = null
})

watch(() => props.modelValue, (v) => {
  if (v) {
    startMic()
    if (!controller) controller = new VoiceCallController({
      onStatus: (s) => {
        controllerStatus.value = s
        uiMode.value = s === 'speaking' ? 'speaking' : 'recognizing'
        // 播放态时切换到播放器的 analyser
        if (s === 'speaking' && (controller as any)?.player?.getAnalyser) {
          try { playAnalyser = (controller as any).player.getAnalyser() } catch {}
          if (playAnalyser) activeAnalyser = playAnalyser
        } else if (micAnalyser) {
          activeAnalyser = micAnalyser
        }
      },
    })
    const chatId = props.chatId || `chat_${Math.random().toString(36).slice(2, 8)}`
    controller.start({ baseURL: props.baseURL || '', chatId }).catch(() => {})
  } else {
    stopMic()
    controller?.stop()
    controller = null
  }
})

watch(() => props.status, () => {
  // 颜色在渲染时读取 props.status
})
</script>

<style scoped>
@keyframes vc-pulse {
  0%, 100% { transform: scaleY(1); opacity: 0.8; }
  50% { transform: scaleY(1.35); opacity: 1; }
}
</style>


